#include "ReliableChannel.h"
#include "NetPacketPool.h"
#include <vector>
#include <cstring>
#include "NetUtils.h"
#include "NetConstants.h"
#include "NetPeer.h"
#include "NetDebug.h"
#include "spdlog/spdlog.h"

#define BitsInByte 8

namespace LiteNetLib
{

    // =========================================== [ PendingPacket ] ===========================================

    PendingPacket::~PendingPacket()
    {
        if(m_packet != nullptr)
        {
            delete m_packet;
        }
    }

    void PendingPacket::init(NetPacket *packet)
    {
        if(m_packet != nullptr)
        {
            NetPacketPool::GetInstance().recycleOrDelete(m_packet);
        }
        m_packet = packet;
        m_isSent = false;
    }

    bool PendingPacket::trySend(int64_t currentTime, NetPeer *netPeer)
    {
        if (m_packet == nullptr)
        {
            return false;
        }
        if (m_isSent) // check send time
        {
            int64_t packetHoldTime = currentTime - m_timeStamp;
            if (packetHoldTime < NetConstants::PacketResendDelay)
            {
                return true;
            }
        }
        m_timeStamp = currentTime;
        m_isSent = true;
        netPeer->sendUserPacket(m_packet);
        return true;
    }

    bool PendingPacket::clear()
    {
        if (m_packet != nullptr)
        {
            NetPacketPool::GetInstance().recycleOrDelete(m_packet);
            m_packet = nullptr;
            return true;
        }
        return false;
    }

    // =========================================== [ ReliableChannel ] ===========================================

    ReliableChannel::ReliableChannel(NetPeer *netPeer, bool ordered, uint8_t id) : 
        BaseChannel(netPeer),
        m_ordered(ordered),
        m_id(id),
        m_localWindowStart(0),
        m_localSeqence(0),
        m_remoteSequence(0),
        m_remoteWindowStart(0),
        m_windowSize(NetConstants::DefaultWindowSize)
    {
        if (m_ordered)
        {
            m_deliveryMethod = DeliveryMethod::ReliableOrdered;
        }
        else
        {
            m_deliveryMethod = DeliveryMethod::ReliableUnordered;
        }
        m_outgoingAcks = new NetPacket(PacketProperty::Ack, (m_windowSize - 1) / BitsInByte + 2);
        // 清空数据，以防冲突
        memset(m_outgoingAcks->getRawData() + 1, 0, m_outgoingAcks->getPacketSize() - 1);
        m_outgoingAcks->setChannelId(m_id);
    }

    ReliableChannel::~ReliableChannel()
    {
        if(m_outgoingAcks != nullptr)
        {
            delete m_outgoingAcks;
        }
        for(int index = 0; index < 64; index++)
        {
            if(m_receivedPackets[index] != nullptr)
            {
                delete m_receivedPackets[index];
            }
        }
    }

    // 清理并回收资源
    void ReliableChannel::clear()
    {
        BaseChannel::clear();
        if(m_outgoingAcks != nullptr)
        {
            delete m_outgoingAcks;
            // NetPacketPool::GetInstance().recycleOrDelete(m_outgoingAcks);
            m_outgoingAcks = nullptr;
        }
        for(int index = 0; index < 64; index++)
        {
            if(m_receivedPackets[index] != nullptr)
            {
                NetPacketPool::GetInstance().recycleOrDelete(m_receivedPackets[index]);
                m_receivedPackets[index] = nullptr;
            }
            m_pendingPackets[index].clear();
        }
    }

    // 处理 ack
    void ReliableChannel::processAck(const char *dataBuff, uint32_t dataSize)
    {
        if (dataSize != m_outgoingAcks->getPacketSize())
        {
            NetDebug::Error("[PA]Invalid acks packet size");
            return;
        }
        uint16_t ackWindowStart = NetPacket::GetSequence(dataBuff);
        int32_t windowRel = NetUtils::RelativeSequenceNumber(m_localWindowStart, ackWindowStart);
        if (ackWindowStart >= NetConstants::MaxSequence || windowRel < 0)
        {
            NetDebug::Error("[PA]Bad window start");
            return;
        }
        // check relevance
        if (windowRel >= m_windowSize)
        {
            NetDebug::Error("[PA]Old acks");
            return;
        }
        std::lock_guard<std::mutex> lk(pendingPacketMutex);

        for (int32_t pendingSeq = m_localWindowStart;
             pendingSeq != m_localSeqence;
             pendingSeq = (pendingSeq + 1) % NetConstants::MaxSequence)
        {
            int32_t rel = NetUtils::RelativeSequenceNumber(pendingSeq, ackWindowStart);
            if (rel >= m_windowSize)
            {
                NetDebug::Error("[PA]REL: {}", rel);
                break;
            }
            int32_t pendingIdx = pendingSeq % m_windowSize;
            int32_t currentByte = NetConstants::ChanneledHeaderSize + pendingIdx / BitsInByte;
            int32_t currentBit = pendingIdx % BitsInByte;
            if ((dataBuff[currentByte] & (1 << currentBit)) == 0)
            {
                NetDebug::Error("[PA]False ack: {}", pendingSeq);
                continue;
            }
            if (pendingSeq == m_localWindowStart)
            {
                // Move window
                m_localWindowStart = (m_localWindowStart + 1) % NetConstants::MaxSequence;
            }
            m_pendingPackets[pendingIdx].clear();
        }

    }

    bool ReliableChannel::sendNextPackets()
    {
        if (m_mustSendAcks)
        {
            m_mustSendAcks = false;
            std::lock_guard<std::mutex> lk(outgoingAckMutex);
            m_netPeer->sendUserPacket(m_outgoingAcks);
        }

        int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
        bool hasPendingPackets = false;

        std::lock_guard<std::mutex> lk(pendingPacketMutex);

        // get packets from queue
        while (m_outgoingQueue.size() > 0)
        {
            int relate = NetUtils::RelativeSequenceNumber(m_localSeqence, m_localWindowStart);
            if (relate >= m_windowSize)
                break;
            NetPacket *netPacket = nullptr;
            if(m_outgoingQueue.try_pop(netPacket) == false)
            {
                NetDebug::Error("LiteNetLib取不出队列的包");
                break;
            }
            netPacket->setSequence((uint16_t)m_localSeqence);
            netPacket->setChannelId(m_id);
            m_pendingPackets[m_localSeqence % m_windowSize].init(netPacket);
            m_localSeqence = (m_localSeqence + 1) % NetConstants::MaxSequence;
        }

        // send
        for (int pendingSeq = m_localWindowStart; pendingSeq != m_localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants::MaxSequence)
        {
            // Please note: TrySend is invoked on a mutable struct, it's important to not extract it into a variable here
            bool hasPacket = m_pendingPackets[pendingSeq % m_windowSize].trySend(currentTime, m_netPeer);
            
            if (hasPacket == true)
            {
                hasPendingPackets = true;
            }
        }
        return hasPendingPackets || m_mustSendAcks || m_outgoingQueue.size() > 0;
    }


    bool ReliableChannel::processData(const char* dataBuff, uint32_t dataSize)
    {
        if (NetPacket::GetProperty(dataBuff) == PacketProperty::Ack)
        {
            processAck(dataBuff, dataSize);
            return false;
        }
        uint16_t seq = NetPacket::GetSequence(dataBuff);
        if (seq >= NetConstants::MaxSequence)
        {
            NetDebug::Error("[RR]Bad sequence");
            return false;
        }
        int32_t relate = NetUtils::RelativeSequenceNumber(seq, m_remoteWindowStart);
        int32_t relateSeq = NetUtils::RelativeSequenceNumber(seq, m_remoteSequence);
        if (relateSeq > m_windowSize)
        {
            NetDebug::Error("[RR]Bad windowSize, relate: {}, seq: {}", relate, seq);
            return false;
        }

        //Drop bad packets
        if (relate < 0)
        {
            //Too old packet doesn't ack
            NetDebug::Error("[RR]ReliableInOrder too old, seq : {} data size : {}, data: {}", seq, dataSize, NetUtils::ToHexString(dataBuff, dataSize));
            return false;
        }
        if (relate >= m_windowSize * 2)
        {
            //Some very new packet
            NetDebug::Error("[RR]ReliableInOrder too new");
            return false;
        }
        //If very new - move window

        int32_t ackIdx;
        int32_t ackByte;
        int32_t ackBit;
        {
            std::lock_guard<std::mutex> outlk(outgoingAckMutex);
            if (relate >= m_windowSize)
            {
                //New window position
                int32_t newWindowStart = (m_remoteWindowStart + relate - m_windowSize + 1) % NetConstants::MaxSequence;
                m_outgoingAcks->setSequence((uint16_t) newWindowStart);
                //Clean old data
                while (m_remoteWindowStart != newWindowStart)
                {
                    ackIdx = m_remoteWindowStart % m_windowSize;
                    ackByte = NetConstants::ChanneledHeaderSize + ackIdx / BitsInByte;
                    ackBit = ackIdx % BitsInByte;
                    m_outgoingAcks->getRawData()[ackByte] &= (uint8_t) ~(1 << ackBit);
                    m_remoteWindowStart = (m_remoteWindowStart + 1) % NetConstants::MaxSequence;
                }
            }
            m_mustSendAcks = true;

            ackIdx = seq % m_windowSize;
            ackByte = NetConstants::ChanneledHeaderSize + ackIdx / BitsInByte;
            ackBit = ackIdx % BitsInByte;
            // NetDebug::Info("outgoing ack: {}", Utils::ToHexString((const uint8_t *)m_outgoingAcks->getRawData(), m_outgoingAcks->getPacketSize()));
            if ((m_outgoingAcks->getRawData()[ackByte] & (1 << ackBit)) != 0)
            {
                NetDebug::Info("[RR]ReliableInOrder duplicate");
                return false;
            }
            //save ack
            m_outgoingAcks->getRawData()[ackByte] |= (uint8_t) (1 << ackBit);
        }

        addToPeerSendQueue();

        NetPacket *netPacket = NetPacketPool::GetInstance().getPacket(dataSize);
        memcpy(netPacket->getRawData(), dataBuff, dataSize);

         //detailed check
        if (seq == m_remoteSequence)
        {
            // NetDebug::Info("[RR]ReliableInOrder packet succes, seq: {}, m_remoteSequence: {}", seq, m_remoteSequence);
            m_netPeer->addReliablePacket(m_deliveryMethod ,netPacket);
            m_remoteSequence = (m_remoteSequence + 1) % NetConstants::MaxSequence;
            if (m_ordered)
            {
                NetPacket *pack = nullptr;
                while ((pack = m_receivedPackets[m_remoteSequence % m_windowSize]) != nullptr)
                {
                    m_receivedPackets[m_remoteSequence % m_windowSize] = nullptr;
                    m_netPeer->addReliablePacket(m_deliveryMethod, pack);
                    m_remoteSequence = (m_remoteSequence + 1) % NetConstants::MaxSequence;
                }
            }
            else
            {
                while (m_earlyReceived[m_remoteSequence % m_windowSize])
                {
                    //process early packet
                    m_earlyReceived[m_remoteSequence % m_windowSize] = false;
                    m_remoteSequence = (m_remoteSequence + 1) % NetConstants::MaxSequence;
                }
            }
            return true;
        }
        else
        {
            // NetDebug::Info("seq error : seq : {}, m_remoteSequence : {}", seq, m_remoteSequence);
        }
        
        if (m_ordered)
        {
            m_receivedPackets[ackIdx] = netPacket;
        }
        else
        {
            m_earlyReceived[ackIdx] = true;
            m_netPeer->addReliablePacket(m_deliveryMethod, netPacket);
        }
        return true;
    }
}
